package com.sove.face.register;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;

import com.alibaba.fastjson2.JSON;
import com.sove.face.Entity.Faces;
import com.sove.face.contant.FaceRegisterConfig;
import com.sove.face.pojo.FaceModel;
import com.sove.face.pojo.FaceModelScore;
import com.sove.face.service.FacesService;

import cn.hutool.core.util.IdUtil;

public class MysqlRegisterFace implements RegisterFace {

	private static Logger logger = LoggerFactory.getLogger(MysqlRegisterFace.class);

	private FaceRegisterConfig faceRegisterConfig;

	@Autowired
	FacesService facesService;

	private ConcurrentHashMap<String, FaceModel> faceCache = new ConcurrentHashMap<>();

	public MysqlRegisterFace(FaceRegisterConfig faceRegisterConfig, FacesService facesService) {
		this.facesService = facesService;
		this.faceRegisterConfig = faceRegisterConfig;

		int size = 1000;
		int pages = 1;
		for (int i = 0; i < pages; i++) {
			Page<Faces> faces = facesService.getFaces(i, size);
			if (faces != null) {
				if (i == 0) {
					pages = faces.getTotalPages();
				}
				List<Faces> data = faces.getContent();
				if (data != null && data.size() > 0) {
					for (Faces face : data) {
						FaceModel faceModel = new FaceModel();
						faceModel.setId(face.getId());
						faceModel.setCreateTime(
								face.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
						faceModel.setFileName(face.getFileName());
						float[] features = JSON.parseObject(face.getFeatures(), float[].class);
						faceModel.setFeatures(features);
						faceCache.put(face.getId(), faceModel);
					}
				}
			}
		}
		logger.trace("加载人脸库:" + faceCache.size());
	}

	@Override
	public List<String> register(List<FaceModel> faceModels) {
		List<String> ids = new ArrayList<>();
		if (faceModels.size() > 0) {
			for (FaceModel faceModel : faceModels) {
				String id = register(faceModel);
				ids.add(id);
			}
		}
		return ids;
	}

	@Override
	public String register(FaceModel faceModel) {
		// 向量数据 长度不等于512 或是1024的不允许注册
		if (faceModel.getFeatures() != null
				&& (faceModel.getFeatures().length == 512 || faceModel.getFeatures().length == 1024)) {
			String id = null;
			try {
				// 根据文件名和向量特征生成ID
				id = IdUtil.objectId();
				Faces face = new Faces();
				face.setId(id);
				
//				ZoneId zoneId = ZoneId.of("Asia/Shanghai");
//				face.setCreateTime(LocalDateTime.now(zoneId));
				
				face.setCreateTime(LocalDateTime.now());
				face.setFileName(faceModel.getFileName());
				face.setFeatures(JSON.toJSONString(faceModel.getFeatures()));
				facesService.save(face);

				faceModel.setId(id);
				faceCache.put(id, faceModel);
				return id;
			} finally {

			}
		}
		logger.warn("注册的faceModel对象数据不规范！ {}", faceModel);
		return null;
	}

	@Override
	public List<FaceModelScore> search(float[] features, int topN, float minimum_score) {
		List<FaceModelScore> list = new ArrayList<>();
		if (minimum_score <= 0.0F) {
			if (faceRegisterConfig.getScore() != null && faceRegisterConfig.getScore() > 0.0F) {
				minimum_score = faceRegisterConfig.getScore();
			} else {
				minimum_score = 0.68F;
			}
		}
		try {
			Iterator<Map.Entry<String, FaceModel>> iterator = faceCache.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, FaceModel> next = iterator.next();
				FaceModel faceModel = next.getValue();
				float score = cosineSimilarity(features, faceModel.getFeatures());
				if (score >= minimum_score) {
					FaceModelScore faceModelScore = new FaceModelScore();
					BeanUtils.copyProperties(faceModel, faceModelScore);
					faceModelScore.setScore(score);
					list.add(faceModelScore);
				}
			}
		} finally {
		}

		List<FaceModelScore> collect = list.stream().sorted(Comparator.comparing(FaceModelScore::getScore).reversed())
				.collect(Collectors.toList());

		if (collect.size() > topN) {
			return collect.subList(0, topN);
		}
		return collect;
	}

	@Override
	public List<FaceModelScore> search(float[] features) {
		int topN;
		float minimum_score;
		if (faceRegisterConfig.getTopN() == null || faceRegisterConfig.getTopN() <= 0) {
			topN = 20;
		} else {
			topN = faceRegisterConfig.getTopN();
		}
		if (faceRegisterConfig.getScore() == null || faceRegisterConfig.getScore() <= 0) {
			minimum_score = 0.68F;
		} else {
			minimum_score = faceRegisterConfig.getScore();
		}
		return search(features, topN, minimum_score);
	}

	public float cosineSimilarity(float[] leftVector, float[] rightVector) {
		double dotProduct = 0.0;

		for (int i = 0; i < leftVector.length; ++i) {
			dotProduct += leftVector[i] * rightVector[i];
		}

		double d1 = 0.0;
		float[] var7 = leftVector;
		int var8 = leftVector.length;

		for (int var9 = 0; var9 < var8; ++var9) {
			float value = var7[var9];
			d1 += Math.pow(value, 2.0);
		}

		double d2 = 0.0;
		float[] var16 = rightVector;
		int var13 = rightVector.length;

		for (int var11 = 0; var11 < var13; ++var11) {
			float value = var16[var11];
			d2 += Math.pow(value, 2.0);
		}

		double cosineSimilarity;
		if (!(d1 <= 0.0) && !(d2 <= 0.0)) {
			cosineSimilarity = dotProduct / (Math.sqrt(d1) * Math.sqrt(d2));
		} else {
			cosineSimilarity = 0.0;
		}

		return (float) cosineSimilarity;
	}

}
